/*
 *  comm.c -- communications functions and more.
 *            Dwayne Fontenot (Jacques@TMI)
 */
#include "std.h"
#include "comm.h"
#include "main.h"
#include "socket_efuns.h"
#include "backend.h"
#include "socket_ctrl.h"
#include "debug.h"
#include "ed.h"
#include "file.h"
#include "master.h"
#include "add_action.h"

static char telnet_break_response[] = { 28, IAC, WILL, TELOPT_TM };
static char telnet_ip_response[] = { 127, IAC, WILL, TELOPT_TM };
static char telnet_abort_response[] = { IAC, DM };
static char telnet_do_tm_response[] = { IAC, WILL, TELOPT_TM };
static char telnet_do_naws[] = { IAC, DO, TELOPT_NAWS };
static char telnet_do_ttype[] = { IAC, DO, TELOPT_TTYPE };
static char telnet_term_query[] =
        { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
static char telnet_no_echo[] = { IAC, WONT, TELOPT_ECHO };
static char telnet_no_single[] = { IAC, WONT, TELOPT_SGA };
static char telnet_yes_echo[] = { IAC, WILL, TELOPT_ECHO };
static char telnet_yes_single[] = { IAC, WILL, TELOPT_SGA };
static char telnet_ga[] = { IAC, GA };
static char telnet_ayt_response[] = { '\n', '[', '-', 'Y', 'e', 's', '-', ']',
        ' ', '\n' };
static char telnet_line_mode[] = { IAC, DO, TELOPT_LINEMODE };
static char telnet_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT
        | MODE_TRAPSIG, IAC, SE };
static char telnet_char_mode[] = { IAC, DONT, TELOPT_LINEMODE };

static char slc_default_flags[] = { SLC_NOSUPPORT, SLC_CANTCHANGE,
        SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_NOSUPPORT,
        SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_CANTCHANGE, SLC_CANTCHANGE,
        SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT,
        SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT };
static char slc_default_chars[] = { 0x00, BREAK, IP, AO, AYT, 0x00, 0x00, 0x00,
        SUSP, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
#ifdef DEBUG
static char *slc_names[] =
{	SLC_NAMELIST};
#endif

/*
 * local function prototypes.
 */
#ifdef SIGNAL_FUNC_TAKES_INT
static void sigpipe_handler(int);
#else
static void sigpipe_handler(void);
#endif

static void hname_handler(void);
static void get_user_data(interactive_t *);
static char *get_user_command(void);
static char *first_cmd_in_buf(interactive_t *);
static int cmd_in_buf(interactive_t *);
static int call_function_interactive(interactive_t *, char *);
static void print_prompt(interactive_t *);
static void query_addr_name(object_t *);
static void got_addr_number(char *, char *);
static void add_ip_entry(long, char *);
static void new_user_handler(int);

#ifdef NO_SNOOP
#  define handle_snoop(str, len, who)
#else
#  define handle_snoop(str, len, who) if ((who)->snooped_by) receive_snoop(str, len, who->snooped_by)

static void receive_snoop(char *, int, object_t * ob);

#endif

/*
 * public local variables.
 */
fd_set readmask, writemask;
int num_user;
#ifdef F_SET_HIDE
int num_hidden_users = 0; /* for the O_HIDDEN flag.  This counter must
 * be kept up to date at all times!  If you
 * modify the O_HIDDEN flag in an object,
 * make sure that you update this counter if
 * the object is interactive. */
#endif
int add_message_calls = 0;
#ifdef F_NETWORK_STATS
int inet_out_packets = 0;
int inet_out_volume = 0;
int inet_in_packets = 0;
int inet_in_volume = 0;
#ifdef PACKAGE_SOCKETS
int inet_socket_in_packets = 0;
int inet_socket_in_volume = 0;
int inet_socket_out_packets = 0;
int inet_socket_out_volume = 0;
#endif
#endif
int inet_packets = 0;
int inet_volume = 0;
interactive_t **all_users = 0;
int max_users = 0;

/*
 * private local variables.
 */
static int addr_server_fd = -1;

static
void set_linemode(interactive_t * ip)
{
	if (ip->iflags & USING_LINEMODE)
	{
		add_binary_message(ip->ob, telnet_line_mode, sizeof(telnet_line_mode));
		add_binary_message(ip->ob, telnet_lm_mode, sizeof(telnet_lm_mode));
	}
	else
	{
		add_binary_message(ip->ob, telnet_no_single, sizeof(telnet_no_single));
	}
}

static
void set_charmode(interactive_t * ip)
{
	if (ip->iflags & USING_LINEMODE)
	{
		add_binary_message(ip->ob, telnet_char_mode, sizeof(telnet_char_mode));
	}
	else
	{
		add_binary_message(ip->ob, telnet_yes_single,
		        sizeof(telnet_yes_single));
	}
}

#ifndef NO_SNOOP
static void receive_snoop(char * buf, int len, object_t * snooper)
{
	/* command giver no longer set to snooper */
#ifdef RECEIVE_SNOOP
	char *str;

	str = new_string(len, "receive_snoop");
	memcpy(str, buf, len);
	str[len] = 0;
	push_malloced_string(str);
	apply(APPLY_RECEIVE_SNOOP, snooper, 1, ORIGIN_DRIVER);
#else
	/* snoop output is now % in all cases */
	add_message(snooper, "%", 1);
	add_message(snooper, buf, len);
#endif
}
#endif

/*
 * Initialize new user connection socket.
 */
void init_user_conn()
{
	struct sockaddr_in sin;
	int sin_len;
	int optval;
	int i;
	int have_fd6;
	int fd6_which;

	/* Check for fd #6 open as a valid socket */
	optval = 1;
	have_fd6 = (setsockopt(6, SOL_SOCKET, SO_REUSEADDR, (char *) &optval,
	        sizeof(optval)) == 0);

	for (i = 0; i < 5; i++)
	{
#ifdef F_NETWORK_STATS
		external_port[i].in_packets = 0;
		external_port[i].in_volume = 0;
		external_port[i].out_packets = 0;
		external_port[i].out_volume = 0;
#endif
		if (!external_port[i].port)
		{
			if (!have_fd6)
				continue;
			fd6_which = i;
			have_fd6 = 0;
			if (FD6_KIND == PORT_UNDEFINED || FD6_PORT < 1)
			{
				debug_message(
				        "Socket passed to fd 6 ignored (support is disabled).\n");
				continue;
			}

			debug_message("Accepting connections on fd 6 (port %d).\n",
			        FD6_PORT);
			external_port[i].kind = FD6_KIND;
			external_port[i].port = FD6_PORT;
			external_port[i].fd = 6;
		}
		else
		{
			/*
			 * create socket of proper type.
			 */
			if ((external_port[i].fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
			{
				debug_perror("init_user_conn: socket", 0);
				exit(1);
			}

			/*
			 * enable local address reuse.
			 */
			optval = 1;
			if (setsockopt(external_port[i].fd, SOL_SOCKET, SO_REUSEADDR,
			        (char *) &optval, sizeof(optval)) == -1)
			{
				socket_perror("init_user_conn: setsockopt", 0);
				exit(2);
			}

			/*
			 * fill in socket address information.
			 */
			sin.sin_family = AF_INET;

			if (MUD_IP[0])
				sin.sin_addr.s_addr = inet_addr(MUD_IP);
			else
				sin.sin_addr.s_addr = INADDR_ANY;

			sin.sin_port = htons((u_short) external_port[i].port);
			/*
			 * bind name to socket.
			 */
			if (bind(external_port[i].fd, (struct sockaddr *) &sin, sizeof(sin))
			        == -1)
			{
				socket_perror("init_user_conn: bind", 0);
				exit(3);
			}
		}
		/*
		 * get socket name.
		 */
		sin_len = sizeof(sin);
		if (getsockname(external_port[i].fd, (struct sockaddr *) &sin, &sin_len)
		        == -1)
		{
			socket_perror("init_user_conn: getsockname", 0);
			if (i != fd6_which)
			{
				exit(4);
			}
		}
		/*
		 * set socket non-blocking,
		 */
		if (set_socket_nonblocking(external_port[i].fd, 1) == -1)
		{
			socket_perror("init_user_conn: set_socket_nonblocking 1", 0);
			if (i != fd6_which)
			{
				exit(8);
			}
		}
		/*
		 * listen on socket for connections.
		 */
		if (listen(external_port[i].fd, 128) == -1)
		{
			socket_perror("init_user_conn: listen", 0);
			if (i != fd6_which)
			{
				exit(10);
			}
		}
	}
	if (have_fd6)
	{
		debug_message("No more ports available; fd #6 ignored.\n");
	}
	/*
	 * register signal handler for SIGPIPE.
	 */
#if !defined(LATTICE) && defined(SIGPIPE)
	if (signal(SIGPIPE, sigpipe_handler) == SIGNAL_ERROR)
	{
		debug_perror("init_user_conn: signal SIGPIPE", 0);
		exit(5);
	}
#endif
}

/*
 * Shut down new user accept file descriptor.
 */
void ipc_remove()
{
	int i;

	for (i = 0; i < 5; i++)
	{
		if (!external_port[i].port)
			continue;
		if (OS_socket_close(external_port[i].fd) == -1)
		{
			socket_perror("ipc_remove: close", 0);
		}
	}

	debug_message("closed external ports\n");
}

void init_addr_server(char * hostname, int addr_server_port)
{
	struct sockaddr_in server;
	struct hostent *hp;
	int server_fd;
	int optval;
	long addr;

	if (addr_server_fd >= 0)
		return;

	if (!hostname)
		return;

	/*
	 * get network host data for hostname.
	 */
	if (hostname[0] >= '0' && hostname[0] <= '9'
	        && (addr = inet_addr(hostname)) != -1)
	{
		hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
	}
	else
	{
		hp = gethostbyname(hostname);
	}
	if (hp == NULL)
	{
		socket_perror("init_addr_server: gethostbyname", 0);
		return;
	}

	/*
	 * set up address information for server.
	 */
	server.sin_family = AF_INET;
	server.sin_port = htons((u_short) addr_server_port);
	server.sin_addr.s_addr = inet_addr(hostname);
	memcpy((char *) &server.sin_addr, (char *) hp->h_addr, hp->h_length);
	/*
	 * create socket of proper type.
	 */
	server_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (server_fd == INVALID_SOCKET)
	{ /* problem opening socket */
		socket_perror("init_addr_server: socket", 0);
		return;
	}
	/*
	 * enable local address reuse.
	 */
	optval = 1;
	if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optval,
	        sizeof(optval)) == -1)
	{
		socket_perror("init_addr_server: setsockopt", 0);
		return;
	}
	/*
	 * connect socket to server address.
	 */
	if (connect(server_fd, (struct sockaddr *) &server, sizeof(server)) == -1)
	{
		if (socket_errno == ECONNREFUSED
		)
			debug_message("Connection to address server (%s %d) refused.\n",
			        hostname, addr_server_port);
		else
			socket_perror("init_addr_server: connect", 0);
		OS_socket_close(server_fd);
		return;
	}
	addr_server_fd = server_fd;
	debug_message("Connected to address server on %s port %d\n", hostname,
	        addr_server_port);
	/*
	 * set socket non-blocking.
	 */
	if (set_socket_nonblocking(server_fd, 1) == -1)
	{
		socket_perror("init_addr_server: set_socket_nonblocking 1", 0);
		return;
	}
}

/*
 * If there is a shadow for this object, then the message should be
 * sent to it. But only if catch_tell() is defined. Beware that one of the
 * shadows may be the originator of the message, which means that we must
 * not send the message to that shadow, or any shadows in the linked list
 * before that shadow.
 *
 * Also note that we don't need to do this in the case of
 * INTERACTIVE_CATCH_TELL, since catch_tell() was already called
 * _instead of_ add_message(), and shadows got their chance then.
 */
#if !defined(INTERACTIVE_CATCH_TELL) && !defined(NO_SHADOWS)
#define SHADOW_CATCH_MESSAGE
#endif

#ifdef SHADOW_CATCH_MESSAGE
static int shadow_catch_message(object_t * ob, char * str)
{
	if (!ob->shadowed)
		return 0;
	while (ob->shadowed != 0 && ob->shadowed != current_object)
		ob = ob->shadowed;
	while (ob->shadowing)
	{
		copy_and_push_string(str);
		if (apply(APPLY_CATCH_TELL, ob, 1, ORIGIN_DRIVER))
			/* this will work, since we know the */
			/* function is defined */
			return 1;
		ob = ob->shadowing;
	}
	return 0;
}
#endif

/*
 * Send a message to an interactive object. If that object is shadowed,
 * special handling is done.
 */
void add_message(object_t * who, char * data, int len)
{
	interactive_t *ip;
	char *cp;
	char *end;

	/*
	 * if who->interactive is not valid, write message on stderr.
	 * (maybe)
	 */
	if (!who || (who->flags & O_DESTRUCTED) || !who->interactive
	        || (who->interactive->iflags & (NET_DEAD | CLOSING)))
	{
#ifdef NONINTERACTIVE_STDERR_WRITE
		putc(']', stderr);
		fwrite(data, len, 1, stderr);
#endif
#ifdef LATTICE
		fflush(stderr);
#endif
		return;
	}
	ip = who->interactive;
#ifdef SHADOW_CATCH_MESSAGE
	/*
	 * shadow handling.
	 */
	if (shadow_catch_message(who, data))
	{
#ifdef SNOOP_SHADOWED
		handle_snoop(data, len, ip);
#endif
		return;
	}
#endif				/* NO_SHADOWS */

	/*
	 * write message into ip->message_buf.
	 */
	end = data + len;
	for (cp = data; cp < end; cp++)
	{
		if (ip->message_length == MESSAGE_BUF_SIZE)
		{
			if (!flush_message(ip))
			{
				debug(connections, ("Broken connection during add_message."));
				return;
			}
			if (ip->message_length == MESSAGE_BUF_SIZE
			)
				break;
		}
		if (*cp == '\n'
#ifndef NO_BUFFER_TYPE
		        && ip->connection_type != PORT_BINARY
#endif
		        )
		{
			if (ip->message_length == (MESSAGE_BUF_SIZE - 1))
			{
				if (!flush_message(ip))
				{
					debug(connections, ("Broken connection during add_message."));
					return;
				}
				if (ip->message_length == (MESSAGE_BUF_SIZE - 1))
					break;
			}
			ip->message_buf[ip->message_producer] = '\r';
			ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE;
			ip->message_length++;
		}
		ip->message_buf[ip->message_producer] = *cp;
		ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE;
		ip->message_length++;
	}

	handle_snoop(data, len, ip);

#ifdef FLUSH_OUTPUT_IMMEDIATELY
	flush_message(ip);
#endif

	add_message_calls++;
} /* add_message() */

/* WARNING: this can only handle results < LARGEST_PRINTABLE_STRING in size */
void add_vmessage(object_t * who, char * format, ...)
{
	int len;
	interactive_t *ip;
	char *cp, new_string_data[LARGEST_PRINTABLE_STRING + 1];
	va_list args;
	V_DCL(char *format);
	V_DCL(object_t *who);

	V_START(args, format);
	V_VAR(object_t *, who, args);
	V_VAR(char *, format, args);
	/*
	 * if who->interactive is not valid, write message on stderr.
	 * (maybe)
	 */
	if (!who || (who->flags & O_DESTRUCTED) || !who->interactive ||
			(who->interactive->iflags & (NET_DEAD | CLOSING)))
	{
#ifdef NONINTERACTIVE_STDERR_WRITE
		putc(']', stderr);
		vfprintf(stderr, format, args);
#endif
		va_end(args);
#ifdef LATTICE
		fflush(stderr);
#endif
		return;
	}
	ip = who->interactive;
	new_string_data[0] = '\0';
	/*
	 * this is dangerous since the data may not all fit into new_string_data
	 * but how to tell if it will without trying it first?  I suppose one
	 * could rewrite vsprintf to accept a maximum length (like strncpy) --
	 * have fun!
	 */
	vsprintf(new_string_data, format, args);
	va_end(args);
	len = strlen(new_string_data);
#ifdef SHADOW_CATCH_MESSAGE
	    /*
	     * shadow handling.
	     */
	    if (shadow_catch_message(who, new_string_data))
	    {
#ifdef SNOOP_SHADOWED
		handle_snoop(new_string_data, len, ip);
#endif
		return;
	}
#endif				/* NO_SHADOWS */

	    /*
	     * write message into ip->message_buf.
	     */
	    for (cp = new_string_data; *cp != '\0'; cp++)
	    {
		    if (ip->message_length == MESSAGE_BUF_SIZE)
		    {
			    if (!flush_message(ip))
			    {
				    debug(connections, ("Broken connection during add_message."));
		return;
	    }
	    if (ip->message_length == MESSAGE_BUF_SIZE)
		break;
	}
	if (*cp == '\n') {
	    if (ip->message_length == (MESSAGE_BUF_SIZE - 1)) {
		if (!flush_message(ip)) {
		    debug(connections, ("Broken connection during add_message.\n"));
		    return;
		}
		if (ip->message_length == (MESSAGE_BUF_SIZE - 1))
		    break;
	    }
	    ip->message_buf[ip->message_producer] = '\r';
	    ip->message_producer = (ip->message_producer + 1)
		% MESSAGE_BUF_SIZE;
	    ip->message_length++;
	}
	ip->message_buf[ip->message_producer] = *cp;
	ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE;
	ip->message_length++;
    }
    if (ip->message_length != 0) {
	if (!flush_message(ip)) {
	    debug(connections, ("Broken connection during add_message.\n"));
	    return;
	}
    }

    handle_snoop(new_string_data, len, ip);

#ifdef FLUSH_OUTPUT_IMMEDIATELY
    flush_message(ip);
#endif
    
    add_message_calls++;
} /* add_message() */

void add_binary_message(object_t * who, char * data, int len)
{
	interactive_t *ip;
	char *cp, *end;

	/*
	 * if who->interactive is not valid, bail
	 */
	if (!who || (who->flags & O_DESTRUCTED) || !who->interactive
	        || (who->interactive->iflags & (NET_DEAD | CLOSING)))
	{
		return;
	}
	ip = who->interactive;

	/*
	 * write message into ip->message_buf.
	 */
	end = data + len;
	for (cp = data; cp < end; cp++)
	{
		if (ip->message_length == MESSAGE_BUF_SIZE)
		{
			if (!flush_message(ip))
			{
				debug(connections, ("Broken connection during add_message."));
				return;
			}
			if (ip->message_length == MESSAGE_BUF_SIZE
			)
				break;
		}
		ip->message_buf[ip->message_producer] = *cp;
		ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE;
		ip->message_length++;
	}

	flush_message(ip);
	add_message_calls++;
}

/*
 * Flush outgoing message buffer of current interactive object.
 */
int flush_message(interactive_t * ip)
{
	int length, num_bytes;

	/*
	 * if ip is not valid, do nothing.
	 */
	if (!ip || (ip->iflags & (NET_DEAD | CLOSING)))
	{
		debug(connections, ("flush_message: invalid target!\n"));
		return 0;
	}
	/*
	 * write ip->message_buf[] to socket.
	 */
	while (ip->message_length != 0)
	{
		if (ip->message_consumer < ip->message_producer)
		{
			length = ip->message_producer - ip->message_consumer;
		}
		else
		{
			length = MESSAGE_BUF_SIZE - ip->message_consumer;
		}
		/* Need to use send to get out of band data
		 num_bytes = write(ip->fd,ip->message_buf + ip->message_consumer,length);
		 */
		num_bytes = send(ip->fd, ip->message_buf + ip->message_consumer, length,
		        ip->out_of_band);
		if (!num_bytes)
		{
			ip->iflags |= NET_DEAD;
			return 0;
		}
		if (num_bytes == -1)
		{
#ifdef EWOULDBLOCK
			if (socket_errno == EWOULDBLOCK)
			{
				debug(connections, ("flush_message: write: Operation would block\n"));
				return 1;
#else
				if (0)
				{
					;
#endif
			}
			else if (socket_errno == EINTR)
			{
				debug(connections, ("flush_message: write: Interrupted system call"));
				return 1;
			}
			else
			{
				socket_perror("flush_message: write", 0);
				ip->iflags |= NET_DEAD;
				return 0;
			}
		}
		ip->message_consumer = (ip->message_consumer + num_bytes)
		        % MESSAGE_BUF_SIZE;
		ip->message_length -= num_bytes;
		ip->out_of_band = 0;
		inet_packets++;
		inet_volume += num_bytes;
#ifdef F_NETWORK_STATS
		inet_out_packets++;
		inet_out_volume += num_bytes;
		external_port[ip->external_port].out_packets++;
		external_port[ip->external_port].out_volume += num_bytes;
#endif
	}
	return 1;
} /* flush_message() */

static void copy_chars(interactive_t * ip, char * from, int num_bytes)
{
	int i, start, x;
	char dont_response[3] = { IAC, DONT, 0 };
	char wont_response[3] = { IAC, WONT, 0 };

	start = ip->text_end;
	for (i = 0; i < num_bytes; i++)
	{
		switch (ip->state)
		{
			case TS_DATA:
				switch ((unsigned char) from[i])
				{
					case IAC:
						ip->state = TS_IAC;
						break;

#if defined(NO_ANSI) && defined(STRIP_BEFORE_PROCESS_INPUT)
					case 0x1b:
						ip->text[ip->text_end++] = 0x20;
						break;
#endif

					case 0x08:
					case 0x7f:
						if (ip->iflags & SINGLE_CHAR
						)
							ip->text[ip->text_end++] = from[i];
						else
						{
							if (ip->text_end > 0)
								ip->text_end--;
						}
						break;

					default:
						ip->text[ip->text_end++] = from[i];
						break;
				}
				break;

			case TS_IAC:
				switch ((unsigned char) from[i])
				{
					case IAC:
						ip->state = TS_DATA;
						ip->text[ip->text_end++] = from[i];
						break;

					case WILL:
						ip->state = TS_WILL;
						break;

					case WONT:
						ip->state = TS_WONT;
						break;

					case DO:
						ip->state = TS_DO;
						break;

					case DONT:
						ip->state = TS_DONT;
						break;

					case SB:
						ip->state = TS_SB;
						ip->sb_pos = 0;
						break;

					case BREAK:
						add_binary_message(ip->ob, telnet_break_response,
						        sizeof(telnet_break_response));
						break;

					case IP: /* interrupt process */
						add_binary_message(ip->ob, telnet_ip_response,
						        sizeof(telnet_ip_response));
						break;

					case AYT: /* are you there?  you bet */
						add_binary_message(ip->ob, telnet_ayt_response,
						        sizeof(telnet_ayt_response));
						break;

					case AO: /* abort output */
						flush_message(ip);
						ip->out_of_band = MSG_OOB;
						add_binary_message(ip->ob, telnet_abort_response,
						        sizeof(telnet_abort_response));
						break;

					default:
						ip->state = TS_DATA;
						break;
				}
				break;

			case TS_WILL:
				ip->iflags |= USING_TELNET;
				switch ((unsigned char) from[i])
				{
					case TELOPT_TTYPE:
						add_binary_message(ip->ob, telnet_term_query,
						        sizeof(telnet_term_query));
						break;

					case TELOPT_LINEMODE:
						/* Do linemode and set the mode: EDIT + TRAPSIG */
						ip->iflags |= USING_LINEMODE;
						set_linemode(ip);
						break;

					case TELOPT_ECHO:
					case TELOPT_NAWS:
						/* do nothing, but don't send a dont response */
						break;

					default:
						dont_response[2] = from[i];
						add_binary_message(ip->ob, dont_response,
						        sizeof(dont_response));
						break;
				}
				ip->state = TS_DATA;
				break;

			case TS_WONT:
				ip->iflags |= USING_TELNET;
				switch ((unsigned char) from[i])
				{
					case TELOPT_LINEMODE:
						/* If we're in single char mode, we just requested for
						 * linemode to be disabled, so don't remove our flag.
						 */
						if (!(ip->iflags & SINGLE_CHAR))
							ip->iflags &= ~USING_LINEMODE;
						break;
				}
				ip->state = TS_DATA;
				break;

			case TS_DO:
				switch ((unsigned char) from[i])
				{
					case TELOPT_TM:
						add_binary_message(ip->ob, telnet_do_tm_response,
						        sizeof(telnet_do_tm_response));
						break;

					case TELOPT_SGA:
						if (ip->iflags & USING_LINEMODE)
						{
							ip->iflags |= SUPPRESS_GA;
							add_binary_message(ip->ob, telnet_yes_single,
							        sizeof(telnet_yes_single));
						}
						else
						{
							if (ip->iflags & SINGLE_CHAR
							)
								add_binary_message(ip->ob, telnet_yes_single,
								        sizeof(telnet_yes_single));
							else
								add_binary_message(ip->ob, telnet_no_single,
								        sizeof(telnet_no_single));
						}
						break;

					case TELOPT_ECHO:
						/* do nothing, but don't send a wont response */
						break;

					default:
						wont_response[2] = from[i];
						add_binary_message(ip->ob, wont_response,
						        sizeof(wont_response));
						break;
				}
				ip->state = TS_DATA;
				break;

			case TS_DONT:
				switch ((unsigned char) from[i])
				{
					case TELOPT_SGA:
						if (ip->iflags & USING_LINEMODE)
						{
							ip->iflags &= ~SUPPRESS_GA;
							add_binary_message(ip->ob, telnet_no_single,
							        sizeof(telnet_no_single));
						}
						break;
				}
				ip->state = TS_DATA;
				break;

			case TS_SB:
				if ((unsigned char) from[i] == IAC)
				{
					ip->state = TS_SB_IAC;
					break;
				}
				if (ip->sb_pos < SB_SIZE - 1)
					ip->sb_buf[ip->sb_pos++] = from[i];
				break;

			case TS_SB_IAC:
				switch ((unsigned char) from[i])
				{
					case IAC:
						if (ip->sb_pos < SB_SIZE - 1)
						{
							ip->sb_buf[ip->sb_pos++] = from[i];
							ip->state = TS_SB;
						}
						break;

					case SE:
						ip->state = TS_DATA;
						ip->sb_buf[ip->sb_pos] = 0;
						switch (ip->sb_buf[0])
						{
							case TELOPT_LINEMODE:
								switch ((unsigned char) ip->sb_buf[1])
								{
									case LM_MODE:
										/* Don't do anything with an ACK */
										if (!(ip->sb_buf[2] & MODE_ACK))
										{
											char sb_ack[] = { IAC, SB,
											        TELOPT_LINEMODE, LM_MODE,
											        MODE_EDIT | MODE_TRAPSIG
											                | MODE_ACK
											                , IAC, SE };

											/* Accept only EDIT and TRAPSIG && force them too */
											add_binary_message(ip->ob, sb_ack,
											        sizeof(sb_ack));
										}
										break;

									case LM_SLC:
									{
										int slc_length = 4;
										char slc_response[SB_SIZE + 6] =
										{	IAC, SB, TELOPT_LINEMODE, LM_SLC};

for(										x = 2; x < ip->sb_pos; x += 3)
										{
											/* no response for an ack */
											if (ip->sb_buf[x + 1] & SLC_ACK)
											continue;

											/* If we get { 0, SLC_DEFAULT, 0 } or { 0, SLC_VARIABLE, 0 } return a list of values */
											/* If it's SLC_DEFAULT, reset to defaults first */
											if (!ip->sb_buf[x] && !ip->sb_buf[x + 2])
											{
												if (ip->sb_buf[x + 1] == SLC_DEFAULT || ip->sb_buf[x + 1] == SLC_VARIABLE)
												{
													int n;

													for (n = 0; n < NSLC; n++)
													{
														slc_response[slc_length++] = n + 1;
														if (ip->sb_buf[x + 1] == SLC_DEFAULT)
														{
															ip->slc[n][0] = slc_default_flags[n];
															ip->slc[n][1] = slc_default_chars[n];
															slc_response[slc_length++] = SLC_DEFAULT;
														}
														else
														{
															slc_response[slc_length++] = ip->slc[n][0];
														}
														slc_response[slc_length++] = ip->slc[n][1];
													}
													break;
												}
											}

											slc_response[slc_length++] = ip->sb_buf[x]--;

											/* If the first octet is out of range, we don't support it */
											/* If the default flag is not supported, we don't support it */
											if (ip->sb_buf[x] >= NSLC || slc_default_flags[(int)ip->sb_buf[x]] == SLC_NOSUPPORT)
											{
												slc_response[slc_length++] = SLC_NOSUPPORT;
												slc_response[slc_length++] = ip->sb_buf[x + 2];
												if ((unsigned char)ip->sb_buf[x + 2] == IAC)
												slc_response[slc_length++] = IAC;
												continue;
											}

											switch ((ip->sb_buf[x + 1] & SLC_LEVELBITS))
											{
												case SLC_NOSUPPORT:
												if (slc_default_flags[(int)ip->sb_buf[x]] == SLC_CANTCHANGE)
												{
													slc_response[slc_length++] = SLC_CANTCHANGE;
													slc_response[slc_length++] = ip->slc[(int)ip->sb_buf[x]][1];
													break;
												}
												slc_response[slc_length++] = SLC_ACK | SLC_NOSUPPORT;
												slc_response[slc_length++] = ip->sb_buf[x + 2];
												ip->slc[(int)ip->sb_buf[x]][0] = SLC_NOSUPPORT;
												ip->slc[(int)ip->sb_buf[x]][1] = 0;
												break;

												case SLC_VARIABLE:
												if (slc_default_flags[(int)ip->sb_buf[x]] == SLC_CANTCHANGE)
												{
													slc_response[slc_length++] = SLC_CANTCHANGE;
													slc_response[slc_length++] = ip->slc[(int)ip->sb_buf[x]][1];
													break;
												}
												slc_response[slc_length++] = SLC_ACK | SLC_VARIABLE;
												slc_response[slc_length++] = ip->sb_buf[x + 2];
												ip->slc[(int)ip->sb_buf[x]][0] = ip->sb_buf[x + 1];
												ip->slc[(int)ip->sb_buf[x]][1] = ip->sb_buf[x + 2];
												break;

												case SLC_CANTCHANGE:
												slc_response[slc_length++] = SLC_ACK | SLC_CANTCHANGE;
												slc_response[slc_length++] = ip->sb_buf[x + 2];
												ip->slc[(int)ip->sb_buf[x]][0] = ip->sb_buf[x + 1];
												ip->slc[(int)ip->sb_buf[x]][1] = ip->sb_buf[x + 2];
												break;

												case SLC_DEFAULT:
												slc_response[slc_length++] = slc_default_flags[(int)ip->sb_buf[x]];
												slc_response[slc_length++] = slc_default_flags[(int)ip->sb_buf[x]];
												ip->slc[(int)ip->sb_buf[x]][0] = slc_default_flags[(int)ip->sb_buf[x]];
												ip->slc[(int)ip->sb_buf[x]][1] = slc_default_chars[(int)ip->sb_buf[x]];
												break;

												default:
												slc_response[slc_length++] = SLC_NOSUPPORT;
												slc_response[slc_length++] = ip->sb_buf[x + 2];
												if ((unsigned char)slc_response[slc_length - 1] == IAC)
												slc_response[slc_length++] = IAC;
												break;
											}
										}

										if (slc_length > 4)
										{
											/* send our response */
											slc_response[slc_length++] = IAC;
											slc_response[slc_length++] = SE;
											add_binary_message(ip->ob,
											        slc_response, slc_length);
										}
									}
										break;

									case DO:
									{
										char sb_wont[] = { IAC, SB,
										        TELOPT_LINEMODE, WONT, 0, IAC,
										        SE };

										/* send back IAC SB TELOPT_LINEMODE WONT x IAC SE */
										sb_wont[4] = ip->sb_buf[2];
										add_binary_message(ip->ob, sb_wont,
										        sizeof(sb_wont));
									}
										break;

									case WILL:
									{
										char sb_dont[] = { IAC, SB,
										        TELOPT_LINEMODE, DONT, 0, IAC,
										        SE };

										/* send back IAC SB TELOPT_LINEMODE DONT x IAC SE */
										sb_dont[4] = ip->sb_buf[2];
										add_binary_message(ip->ob, sb_dont,
										        sizeof(sb_dont));
									}
										break;
								}
								break;

							case TELOPT_NAWS:
								if (ip->sb_pos >= 5)
								{
									push_number(
									        ((unsigned char) ip->sb_buf[1] << 8)
									                | (unsigned char) ip->sb_buf[2]);
									push_number(
									        ((unsigned char) ip->sb_buf[3] << 8)
									                | (unsigned char) ip->sb_buf[4]);
									apply(APPLY_WINDOW_SIZE, ip->ob, 2,
									        ORIGIN_DRIVER);
								}
								break;

							case TELOPT_TTYPE:
								if (!ip->sb_buf[1])
								{
									copy_and_push_string(ip->sb_buf + 2);
									apply(APPLY_TERMINAL_TYPE, ip->ob, 1,
									        ORIGIN_DRIVER);
								}
								break;

							default:
								for (x = 0; x < ip->sb_pos; x++)
									ip->sb_buf[x] = (
									        ip->sb_buf[x] ?
									                ip->sb_buf[x] : 'I');
								copy_and_push_string(ip->sb_buf);
								apply(APPLY_TELNET_SUBOPTION, ip->ob, 1,
								        ORIGIN_DRIVER);
								break;
						}
						break;

					default:
						/*
						 * Apparently, old versions of MudOS would revert to TS_DATA here.
						 * Later versions handle the IAC, and then go back to TS_DATA mode.
						 * I don't think either is proper, but the related RFC documents
						 * aren't clear on what to do (854, 855).  It is my feeling, that
						 * the safest thing to do here is to ignore the option.
						 * -- Marius, 6-Jun-2000
						 */
						ip->state = TS_SB;
						break;
				}
				break;
		}
	}

	if (ip->text_end > start)
	{
		/* handle snooping - snooper does not see type-ahead due to
		 telnet being in linemode */
		if (!(ip->iflags & NOECHO))
			handle_snoop(ip->text + start, ip->text_end - start, ip);
	}
}

/*
 * Read pending data for a user into user->interactive->text.
 * This also does telnet negotiation.
 */
static void get_user_data(interactive_t * ip)
{
	int num_bytes, text_space;
	char buf[MAX_TEXT];

	/* compute how much data we can read right now */
	switch (ip->connection_type)
	{
		case PORT_TELNET:
			text_space = MAX_TEXT - ip->text_end;

			/* check if we need more space */
			if (text_space < MAX_TEXT / 16)
			{
				if (ip->text_start > 0)
				{
					memmove(ip->text, ip->text + ip->text_start,
					        ip->text_end - ip->text_start);
					text_space += ip->text_start;
					ip->text_end -= ip->text_start;
					ip->text_start = 0;
				}

				if (text_space < MAX_TEXT / 16)
				{
					/* the user is sending too much data.  flush it */
					ip->iflags |= SKIP_COMMAND;
					ip->text_start = ip->text_end = 0;
					text_space = MAX_TEXT;
				}
			}
			break;

		case PORT_MUD:
			if (ip->text_end < 4)
				text_space = 4 - ip->text_end;
			else
				text_space = *(int *) ip->text - ip->text_end + 4;
			break;

		default:
			text_space = sizeof(buf);
			break;
	}

	/* read the data from the socket */
	debug(connections, ("get_user_data: read on fd %d\n", ip->fd));
	num_bytes = OS_socket_read(ip->fd, buf, text_space);

	if (!num_bytes)
	{
		if (ip->iflags & CLOSING
		)
			debug_message("get_user_data: tried to read from closing fd.\n");
		ip->iflags |= NET_DEAD;
		remove_interactive(ip->ob, 0);
		return;
	}

	if (num_bytes == -1)
	{
#ifdef EWOULDBLOCK
		if (socket_errno == EWOULDBLOCK)
		{
			debug(connections, ("get_user_data: read on fd %d: Operation would block.\n", ip->fd));
			return;
		}
#endif
		debug_message("get_user_data: read on fd %d\n", ip->fd);
		socket_perror("get_user_data: read", 0);
		ip->iflags |= NET_DEAD;
		remove_interactive(ip->ob, 0);
		return;
	}

#ifdef F_NETWORK_STATS
	inet_in_packets++;
	inet_in_volume += num_bytes;
	external_port[ip->external_port].in_packets++;
	external_port[ip->external_port].in_volume += num_bytes;
#endif

	/* process the data that we've just read */
	switch (ip->connection_type)
	{
		case PORT_TELNET:
			copy_chars(ip, buf, num_bytes);
			if (cmd_in_buf(ip))
				ip->iflags |= CMD_IN_BUF;
			break;

		case PORT_MUD:
			memcpy(ip->text + ip->text_end, buf, num_bytes);
			ip->text_end += num_bytes;

			if (num_bytes == text_space)
			{
				if (ip->text_end == 4)
				{
					*(int *) ip->text = ntohl(*(int *) ip->text);
					if (*(int *) ip->text > MAX_TEXT - 5)
						remove_interactive(ip->ob, 0);
				}
				else
				{
					svalue_t value;

					ip->text[ip->text_end] = 0;
					if (restore_svalue(ip->text + 4, &value) == 0)
					{
						STACK_INC;
						*sp = value;
					}
					else
					{
						push_undefined();
					}
					ip->text_end = 0;
					apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER);
				}
			}
			break;

		case PORT_ASCII:
		{
			char *nl, *p;

			memcpy(ip->text + ip->text_end, buf, num_bytes);
			ip->text_end += num_bytes;

			p = ip->text + ip->text_start;
			while ((nl = memchr(p, '\n', ip->text_end - ip->text_start)))
			{
				ip->text_start = (nl + 1) - ip->text;

				*nl = 0;
				if (*(nl - 1) == '\r')
					*--nl = 0;

				if (!(ip->ob->flags & O_DESTRUCTED))
				{
					char *str;

					str = new_string(nl - p, "PORT_ASCII");
					memcpy(str, p, nl - p + 1);
					push_malloced_string(str);
					apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER);
				}

				if (ip->text_start == ip->text_end)
				{
					ip->text_start = ip->text_end = 0;
					break;
				}

				p = nl + 1;
			}
		}
			break;

#ifndef NO_BUFFER_TYPE
		case PORT_BINARY:
		{
			buffer_t *buffer;

			buffer = allocate_buffer(num_bytes);
			memcpy(buffer->item, buf, num_bytes);

			push_refed_buffer(buffer);
			apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER);
		}
			break;
#endif
	}
}

static int clean_buf(interactive_t * ip)
{
	/* skip null input */
	while (ip->text_start < ip->text_end && !*(ip->text + ip->text_start))
		ip->text_start++;

	/* if we've advanced beyond the end of the buffer, reset it */
	if (ip->text_start >= ip->text_end)
	{
		ip->text_start = ip->text_end = 0;
	}

	/* if we're skipping the current command, check to see if it has been
	 completed yet.  if it has, flush it and clear the skip bit */
	if (ip->iflags & SKIP_COMMAND)
	{
		char *p;

		for (p = ip->text + ip->text_start; p < ip->text + ip->text_end; p++)
		{
			if (*p == '\r' || *p == '\n')
			{
				ip->text_start += p - (ip->text + ip->text_start) + 1;
				ip->iflags &= ~SKIP_COMMAND;
				return clean_buf(ip);
			}
		}
	}

	return (ip->text_end > ip->text_start);
}

static int cmd_in_buf(interactive_t * ip)
{
	char *p;

	/* do standard input buffer cleanup */
	if (!clean_buf(ip))
		return 0;

	/* if we're in single character mode, we've got input */
	if (ip->iflags & SINGLE_CHAR
	)
		return 1;

	/* search for a newline.  if found, we have a command */
	for (p = ip->text + ip->text_start; p < ip->text + ip->text_end; p++)
	{
		if (*p == '\r' || *p == '\n')
			return 1;
	}

	/* duh, no command */
	return 0;
}

static char *first_cmd_in_buf(interactive_t * ip)
{
	char *p;
#ifdef GET_CHAR_IS_BUFFERED
	static char tmp[2];
#endif

	/* do standard input buffer cleanup */
	if (!clean_buf(ip))
		return 0;

	p = ip->text + ip->text_start;

	/* if we're in single character mode, we've got input */
	if (ip->iflags & SINGLE_CHAR)
	{
		if (*p == 8 || *p == 127)
			*p = 0;
#ifndef GET_CHAR_IS_BUFFERED
		ip->text_start++;
		if (!clean_buf(ip))
			ip->iflags &= ~CMD_IN_BUF;
		return p;
#else
		tmp[0] = *p;
		ip->text[ip->text_start++] = 0;
		if (!clean_buf(ip))
		ip->iflags &= ~CMD_IN_BUF;
		return tmp;
#endif
	}

	/* search for the newline */
	while (ip->text[ip->text_start] != '\n' && ip->text[ip->text_start] != '\r')
		ip->text_start++;

	/* check for "\r\n" or "\n\r" */
	if (ip->text_start + 1 < ip->text_end
	        && ((ip->text[ip->text_start] == '\r'
	                && ip->text[ip->text_start + 1] == '\n')
	                || (ip->text[ip->text_start] == '\n'
	                        && ip->text[ip->text_start + 1] == '\r')))
	{
		ip->text[ip->text_start++] = 0;
	}

	ip->text[ip->text_start++] = 0;
	if (!cmd_in_buf(ip))
		ip->iflags &= ~CMD_IN_BUF;

	return p;
}
/*
 * SIGPIPE handler -- does very little for now.
 */
#ifdef SIGNAL_FUNC_TAKES_INT
void sigpipe_handler(int sig)
#else
void sigpipe_handler()
#endif
{
	debug(connections, ("SIGPIPE received."));
	signal(SIGPIPE, sigpipe_handler);
} /* sigpipe_handler() */

/*
 * SIGALRM handler.
 */
#ifdef SIGNAL_FUNC_TAKES_INT
void sigalrm_handler(int sig)
#else
void sigalrm_handler()
#endif
{
	heart_beat_flag = 1;
} /* sigalrm_handler() */

INLINE void make_selectmasks()
{
	int i;

	/*
	 * generate readmask and writemask for select() call.
	 */
	FD_ZERO(&readmask);
	FD_ZERO(&writemask);
	/*
	 * set new user accept fd in readmask.
	 */
	for (i = 0; i < 5; i++)
	{
		if (!external_port[i].port)
			continue;
		FD_SET(external_port[i].fd, &readmask);
	}
	/*
	 * set user fds in readmask.
	 */
	for (i = 0; i < max_users; i++)
	{
		if (!all_users[i] || (all_users[i]->iflags & (CLOSING | CMD_IN_BUF)))
			continue;
		/*
		 * if this user needs more input to make a complete command, set his
		 * fd so we can get it.
		 */
		FD_SET(all_users[i]->fd, &readmask);
		if (all_users[i]->message_length != 0)
			FD_SET(all_users[i]->fd, &writemask);
	}
	/*
	 * if addr_server_fd is set, set its fd in readmask.
	 */
	if (addr_server_fd >= 0)
	{
		FD_SET(addr_server_fd, &readmask);
	}
#if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL)
	/*
	 * set fd's for efun sockets.
	 */
	for (i = 0; i < max_lpc_socks; i++)
	{
		if (lpc_socks[i].state != STATE_CLOSED)
		{
			if (lpc_socks[i].state != STATE_FLUSHING
			        && (lpc_socks[i].flags & S_WACCEPT) == 0)
				FD_SET(lpc_socks[i].fd, &readmask);
			if (lpc_socks[i].flags & S_BLOCKED
			)
				FD_SET(lpc_socks[i].fd, &writemask);
		}
	}
#endif
} /* make_selectmasks() */

/*
 * Process I/O.
 */INLINE void process_io()
{
	int i;

	/*
	 * check for new user connection.
	 */
	for (i = 0; i < 5; i++)
	{
		if (!external_port[i].port)
			continue;
		if (FD_ISSET(external_port[i].fd, &readmask))
		{
			debug(connections, ("process_io: NEW_USER\n"));
			new_user_handler(i);
		}
	}
	/*
	 * check for data pending on user connections.
	 */
	for (i = 0; i < max_users; i++)
	{
		if (!all_users[i] || (all_users[i]->iflags & (CLOSING | CMD_IN_BUF)))
			continue;
		if (all_users[i]->iflags & NET_DEAD)
		{
			remove_interactive(all_users[i]->ob, 0);
			continue;
		}
		if (FD_ISSET(all_users[i]->fd, &readmask))
		{
			debug(connections, ("process_io: USER %d\n", i));
			get_user_data(all_users[i]);
			if (!all_users[i])
				continue;
		}
		if (FD_ISSET(all_users[i]->fd, &writemask))
			flush_message(all_users[i]);
	}
#if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL)
	/*
	 * check for data pending on efun socket connections.
	 */
	for (i = 0; i < max_lpc_socks; i++)
	{
		if (lpc_socks[i].state != STATE_CLOSED)
			if (FD_ISSET(lpc_socks[i].fd, &readmask))
				socket_read_select_handler(i);
		if (lpc_socks[i].state != STATE_CLOSED)
			if (FD_ISSET(lpc_socks[i].fd, &writemask))
				socket_write_select_handler(i);
	}
#endif
	/*
	 * check for data pending from address server.
	 */
	if (addr_server_fd >= 0)
	{
		if (FD_ISSET(addr_server_fd, &readmask))
		{
			debug(connections, ("process_io: IP_DAEMON\n"));
			hname_handler();
		}
	}
}

/*
 * This is the new user connection handler. This function is called by the
 * event handler when data is pending on the listening socket (new_user_fd).
 * If space is available, an interactive data structure is initialized and
 * the user is connected.
 */
static void new_user_handler(int which)
{
	int new_socket_fd;
	struct sockaddr_in addr;
	int length;
	int i, x;
	object_t *master, *ob;
	svalue_t *ret;

	length = sizeof(addr);
	debug(connections, ("new_user_handler: accept on fd %d\n", external_port[which].fd));
	new_socket_fd = accept(external_port[which].fd, (struct sockaddr *) &addr,
	        (int *) &length);
	if (new_socket_fd < 0)
	{
#ifdef EWOULDBLOCK
		if (socket_errno == EWOULDBLOCK)
		{
			debug(connections, ("new_user_handler: accept: Operation would block\n"));
		}
		else
		{
#else
			if (1)
			{
#endif
			socket_perror("new_user_handler: accept", 0);
		}
		return;
	}

	/*
	 * according to Amylaar, 'accepted' sockets in Linux 0.99p6 don't
	 * properly inherit the nonblocking property from the listening socket.
	 * Marius, 19-Jun-2000: this happens on other platforms as well, so just
	 * do it for everyone
	 */
	if (set_socket_nonblocking(new_socket_fd, 1) == -1)
	{
		socket_perror("new_user_handler: set_socket_nonblocking 1", 0);
		OS_socket_close(new_socket_fd);
		return;
	}

	/* find the first available slot */
	for (i = 0; i < max_users; i++)
		if (!all_users[i])
			break;

	if (i == max_users)
	{
		if (all_users)
		{
			all_users = RESIZE(all_users, max_users + 10, interactive_t *,
					TAG_USERS, "new_user_handler");
		}
		else
		{
			all_users = CALLOCATE(10, interactive_t *,
					TAG_USERS, "new_user_handler");
		}
		while (max_users < i + 10)
			all_users[max_users++] = 0;
	}

	set_command_giver(master_ob);
	master_ob->interactive =
	        (interactive_t *) DXALLOC(sizeof(interactive_t), TAG_INTERACTIVE,
			        "new_user_handler");
#ifndef NO_ADD_ACTION
	master_ob->interactive->default_err_message.s = 0;
#endif
	master_ob->interactive->connection_type = external_port[which].kind;
	master_ob->flags |= O_ONCE_INTERACTIVE;
	/*
	 * initialize new user interactive data structure.
	 */
	master_ob->interactive->ob = master_ob;
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
	master_ob->interactive->input_to = 0;
#endif
	master_ob->interactive->iflags = 0;
	master_ob->interactive->text[0] = '\0';
	master_ob->interactive->text_end = 0;
	master_ob->interactive->text_start = 0;
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
	master_ob->interactive->carryover = NULL;
	master_ob->interactive->num_carry = 0;
#endif
#ifndef NO_SNOOP
	master_ob->interactive->snooped_by = 0;
#endif
	master_ob->interactive->last_time = current_time;
#ifdef TRACE
	master_ob->interactive->trace_level = 0;
	master_ob->interactive->trace_prefix = 0;
#endif
#ifdef OLD_ED
	master_ob->interactive->ed_buffer = 0;
#endif
	master_ob->interactive->message_producer = 0;
	master_ob->interactive->message_consumer = 0;
	master_ob->interactive->message_length = 0;
	master_ob->interactive->state = TS_DATA;
	master_ob->interactive->out_of_band = 0;
	for (x = 0; x < NSLC; x++)
	{
		master_ob->interactive->slc[x][0] = slc_default_flags[x];
		master_ob->interactive->slc[x][1] = slc_default_chars[x];
	}
	all_users[i] = master_ob->interactive;
	all_users[i]->fd = new_socket_fd;
#ifdef F_QUERY_IP_PORT
	all_users[i]->local_port = external_port[which].port;
#endif
#ifdef F_NETWORK_STATS
	all_users[i]->external_port = which;
#endif
	set_prompt("> ");

	memcpy((char *) &all_users[i]->addr, (char *) &addr, length);
	debug(connections, ("New connection from %s.\n", inet_ntoa(addr.sin_addr)));
	num_user++;
	/*
	 * The user object has one extra reference. It is asserted that the
	 * master_ob is loaded.  Save a pointer to the master ob incase it
	 * changes during APPLY_CONNECT.  We want to free the reference on
	 * the right copy of the object.
	 */
	master = master_ob;
	add_ref(master_ob, "new_user");
	push_number(external_port[which].port);
	ret = apply_master_ob(APPLY_CONNECT, 1);
	/* master_ob->interactive can be zero if the master object self
	 destructed in the above (don't ask) */
	set_command_giver(0);
	if (ret == 0 || ret == (svalue_t *) -1 || ret->type != T_OBJECT
	        || !master_ob->interactive)
	{
		if (master_ob->interactive)
			remove_interactive(master_ob, 0);
		else
			free_object(master, "new_user");
		debug_message("Connection from %s aborted.\n",
		        inet_ntoa(addr.sin_addr));
		return;
	}
	/*
	 * There was an object returned from connect(). Use this as the user
	 * object.
	 */
	free_object(master, "new_user");
	ob = ret->u.ob;
#ifdef F_SET_HIDE
	if (ob->flags & O_HIDDEN
	)
		num_hidden_users++;
#endif
	ob->interactive = master_ob->interactive;
	ob->interactive->ob = ob;
	ob->flags |= O_ONCE_INTERACTIVE;
	/*
	 * assume the existance of write_prompt and process_input in user.c
	 * until proven wrong (after trying to call them).
	 */
	ob->interactive->iflags |= (HAS_WRITE_PROMPT | HAS_PROCESS_INPUT);

	master_ob->flags &= ~O_ONCE_INTERACTIVE;
	master_ob->interactive = 0;
	add_ref(ob, "new_user");
	set_command_giver(ob);
	if (addr_server_fd >= 0)
	{
		query_addr_name(ob);
	}

	if (external_port[which].kind == PORT_TELNET)
	{
		/* Ask permission to ask them for their terminal type */
		add_binary_message(ob, telnet_do_ttype, sizeof(telnet_do_ttype));
		/* Ask them for their window size */
		add_binary_message(ob, telnet_do_naws, sizeof(telnet_do_naws));
	}

	logon(ob);
	debug(connections, ("new_user_handler: end\n"));
	set_command_giver(0);
} /* new_user_handler() */

/*
 * Return the first command of the next user in sequence that has a complete
 * command in their buffer.  A command is defined to be a single character
 * when SINGLE_CHAR is set, or a newline terminated string otherwise.
 */
static char *get_user_command()
{
	static int NextCmdGiver = 0;

	int i;
	interactive_t *ip;
	char *user_command = 0;

	/* find and return a user command */
	for (i = 0; i < max_users; i++)
	{
		ip = all_users[NextCmdGiver++];
		NextCmdGiver %= max_users;

		if (!ip || !ip->ob || ip->ob->flags & O_DESTRUCTED
		)
			continue;

		/* if we've got text to send, try to flush it, could lose the link here */
		if (ip->message_length)
		{
			object_t *ob = ip->ob;
			flush_message(ip);
			if (!IP_VALID(ip, ob))
				continue;
		}

		/* if there's a command in the buffer, pull it out! */
		if (ip->iflags & CMD_IN_BUF)
		{
			NextCmdGiver++;
			NextCmdGiver %= max_users;
			user_command = first_cmd_in_buf(ip);
			break;
		}
	}

	/* no command found - return 0 */
	if (!user_command)
		return 0;

	/* got a command - return it and set command_giver */
	debug(connections, ("get_user_command: user_command = (%s)\n", user_command));
	save_command_giver(ip->ob);

#ifndef GET_CHAR_IS_BUFFERED
	if (ip->iflags & NOECHO)
	{
#else
		if ((ip->iflags & NOECHO) && !(ip->iflags & SINGLE_CHAR))
		{
#endif
		/* must not enable echo before the user input is received */
		add_binary_message(command_giver, telnet_no_echo,
		        sizeof(telnet_no_echo));
		ip->iflags &= ~NOECHO;
	}

	ip->last_time = current_time;
	return user_command;
} /* get_user_command() */

static int escape_command(interactive_t * ip, char * user_command)
{
	if (user_command[0] != '!')
		return 0;
#ifdef OLD_ED
	if (ip->ed_buffer)
	return 1;
#endif
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
	if (ip->input_to && !(ip->iflags & NOESC))
		return 1;
#endif
	return 0;
}

static void process_input(interactive_t * ip, char * user_command)
{
	svalue_t *ret;

	if (!(ip->iflags & HAS_PROCESS_INPUT))
	{
		parse_command(user_command, command_giver);
		return;
	}

	/*
	 * send a copy of user input back to user object to provide
	 * support for things like command history and mud shell
	 * programming languages.
	 */
	copy_and_push_string(user_command);
	ret = apply(APPLY_PROCESS_INPUT, command_giver, 1, ORIGIN_DRIVER);
	if (!IP_VALID(ip, command_giver))
		return;
	if (!ret)
	{
		ip->iflags &= ~HAS_PROCESS_INPUT;
		parse_command(user_command, command_giver);
		return;
	}

#ifndef NO_ADD_ACTION
	if (ret->type == T_STRING)
	{
		static char buf[MAX_TEXT];

		strncpy(buf, ret->u.string, MAX_TEXT - 1);
		parse_command(buf, command_giver);
	}
	else
	{
		if (ret->type != T_NUMBER || !ret->u.number)
		parse_command(user_command, command_giver);
	}
#endif
}

/*
 * This is the user command handler. This function is called when
 * a user command needs to be processed.
 * This function calls get_user_command() to get a user command.
 * One user command is processed per execution of this function.
 */
int process_user_command()
{
	char *user_command;
	interactive_t *ip;

	/*
	 * WARNING: get_user_command() sets command_giver via
	 * save_command_giver(), but only when the return is non-zero!
	 */
	if (!(user_command = get_user_command()))
		return 0;

	ip = command_giver->interactive;
	current_interactive = command_giver; /* this is yuck phooey, sigh */
	clear_notify(ip->ob);
	update_load_av();
	debug(connections, ("process_user_command: command_giver = /%s\n", command_giver->name));

	if (escape_command(ip, user_command))
	{
		if (ip->iflags & SINGLE_CHAR)
		{
			/* only 1 char ... switch to line buffer mode */
			ip->iflags |= WAS_SINGLE_CHAR;
			ip->iflags &= ~SINGLE_CHAR;
#ifdef GET_CHAR_IS_BUFFERED
			ip->text_start = ip->text_end = *ip->text = 0;
#endif
			set_linemode(ip);
		}
		else
		{
			if (ip->iflags & WAS_SINGLE_CHAR)
			{
				/* we now have a string ... switch back to char mode */
				ip->iflags &= ~WAS_SINGLE_CHAR;
				ip->iflags |= SINGLE_CHAR;
				set_charmode(ip);
				if (!IP_VALID(ip, command_giver))
				{
					goto exit;
				}
			}

			process_input(ip, user_command + 1);
		}

		goto exit;
	}

#ifdef OLD_ED
	if (ip->ed_buffer)
	{
		ed_cmd(user_command);
		goto exit;
	}
#endif

#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
	if (call_function_interactive(ip, user_command))
	{
		goto exit;
	}
#endif

	process_input(ip, user_command);

	exit:
	/*
	 * Print a prompt if user is still here.
	 */
	if (IP_VALID(ip, command_giver))
		print_prompt(ip);

	current_interactive = 0;
	restore_command_giver();
	return 1;
}

#define HNAME_BUF_SIZE 200
/*
 * This is the hname input data handler. This function is called by the
 * master handler when data is pending on the hname socket (addr_server_fd).
 */

static void hname_handler()
{
	static char hname_buf[HNAME_BUF_SIZE];
	static int hname_buf_pos;
	int num_bytes;

	num_bytes = HNAME_BUF_SIZE - hname_buf_pos - 1; /* room for nul */
	num_bytes =
	        OS_socket_read(addr_server_fd, hname_buf + hname_buf_pos, num_bytes);
	if (num_bytes <= 0)
	{
		if (num_bytes == -1)
		{
#ifdef EWOULDBLOCK
			if (socket_errno == EWOULDBLOCK)
			{
				debug(connections, ("hname_handler: read on fd %d: Operation would block.\n",
								addr_server_fd));
				return;
			}
#endif
			debug_message("hname_handler: read on fd %d\n", addr_server_fd);
			socket_perror("hname_handler: read", 0);
		}
		else
		{
			debug_message(
			        "hname_handler: closing address server connection.\n");
		}OS_socket_close(addr_server_fd);
		addr_server_fd = -1;
		return;
	}

	hname_buf_pos += num_bytes;
	hname_buf[hname_buf_pos] = 0;
	debug(connections, ("hname_handler: address server replies: %s", hname_buf));

	while (hname_buf_pos)
	{
		char *nl, *pp;

		/* if there's no newline, there's more data to come */
		if (!(nl = strchr(hname_buf, '\n')))
			break;
		*nl++ = 0;

		if ((pp = strchr(hname_buf, ' ')) != 0)
		{
			*pp++ = 0;
			got_addr_number(pp, hname_buf);

			if (isdigit(hname_buf[0]))
			{
				unsigned long laddr;

				if ((laddr = inet_addr(hname_buf)) != INADDR_NONE)
				{
					if (strcmp(pp, "0") != 0)
						add_ip_entry(laddr, pp);
				}
			}
		}

		hname_buf_pos -= (nl - hname_buf);
		if (hname_buf_pos)
			memmove(hname_buf, nl, hname_buf_pos + 1); /* be sure to get the nul */
	}
}

/*
 * Remove an interactive user immediately.
 */
void remove_interactive(object_t * ob, int dested)
{
	int idx;
	/* don't have to worry about this dangling, since this is the routine
	 * that causes this to dangle elsewhere, and we are protected from
	 * getting called recursively by CLOSING.  safe_apply() should be
	 * used here, since once we start this process we can't back out,
	 * so jumping out with an error would be bad.
	 */
	interactive_t *ip = ob->interactive;

	if (!ip)
		return;

	if (ip->iflags & CLOSING)
	{
		if (!dested)
			debug_message("Double call to remove_interactive()\n");
		return;
	}

	debug(connections, ("Closing connection from %s.\n",
					inet_ntoa(ip->addr.sin_addr)));

	flush_message(ip);
	ip->iflags |= CLOSING;

#ifdef OLD_ED
	if (ip->ed_buffer)
	{
		save_ed_buffer(ob);
	}
#else
	if (ob->flags & O_IN_EDIT)
	{
		object_save_ed_buffer(ob);
		ob->flags &= ~O_IN_EDIT;
	}
#endif

	if (!dested)
	{
		/*
		 * auto-notification of net death
		 */
		save_command_giver(ob);
		safe_apply(APPLY_NET_DEAD, ob, 0, ORIGIN_DRIVER);
		restore_command_giver();
	}

#ifndef NO_SNOOP
	if (ip->snooped_by)
	{
		ip->snooped_by->flags &= ~O_SNOOP;
		ip->snooped_by = 0;
	}
#endif

	debug(connections, ("remove_interactive: closing fd %d\n", ip->fd));
	if (OS_socket_close(ip->fd) == -1)
	{
		socket_perror("remove_interactive: close", 0);
	}
#ifdef F_SET_HIDE
	if (ob->flags & O_HIDDEN
	)
		num_hidden_users--;
#endif
	num_user--;
	clear_notify(ip->ob);
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
	if (ip->input_to)
	{
		free_object(ip->input_to->ob, "remove_interactive");
		free_sentence(ip->input_to);
		if (ip->num_carry > 0)
			free_some_svalues(ip->carryover, ip->num_carry);
		ip->carryover = NULL;
		ip->num_carry = 0;
		ip->input_to = 0;
	}
#endif
	for (idx = 0; idx < max_users; idx++)
		if (all_users[idx] == ip)
			break;DEBUG_CHECK(idx == max_users, "remove_interactive: could not find and remove user!\n");
	FREE(ip);
	ob->interactive = 0;
	all_users[idx] = 0;
	free_object(ob, "remove_interactive");
	return;
} /* remove_interactive() */

#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
static int call_function_interactive(interactive_t * i, char * str)
{
	object_t *ob;
	funptr_t *funp;
	char *function;
	svalue_t *args;
	sentence_t *sent;
	int num_arg;
#ifdef GET_CHAR_IS_BUFFERED
	int was_single = 0;
	int was_noecho = 0;
#endif

	i->iflags &= ~NOESC;
	if (!(sent = i->input_to))
		return (0);

	/*
	 * Special feature: input_to() has been called to setup a call to a
	 * function.
	 */
	if (sent->ob->flags & O_DESTRUCTED)
	{
		/* Sorry, the object has selfdestructed ! */
		free_object(sent->ob, "call_function_interactive");
		free_sentence(sent);
		i->input_to = 0;
		if (i->num_carry)
			free_some_svalues(i->carryover, i->num_carry);
		i->carryover = NULL;
		i->num_carry = 0;
		return (0);
	}
	/*
	 * We must all references to input_to fields before the call to apply(),
	 * because someone might want to set up a new input_to().
	 */
	free_object(sent->ob, "call_function_interactive");
	/* we put the function on the stack in case of an error */STACK_INC;
	if (sent->flags & V_FUNCTION)
	{
		function = 0;
		sp->type = T_FUNCTION;
		sp->u.fp = funp = sent->function.f;
		funp->hdr.ref++;
	}
	else
	{
		sp->type = T_STRING;
		sp->subtype = STRING_SHARED;
		sp->u.string = function = sent->function.s;
		ref_string(function);
	}
	ob = sent->ob;
	free_sentence(sent);

	/*
	 * If we have args, we have to copy them, so the svalues on the
	 * interactive struct can be FREEd
	 */
	num_arg = i->num_carry;
	if (num_arg)
	{
		args = i->carryover;
		i->num_carry = 0;
		i->carryover = NULL;
	}
	else
		args = NULL;

	i->input_to = 0;
	if (i->iflags & SINGLE_CHAR)
	{
		/*
		 * clear single character mode
		 */
		i->iflags &= ~SINGLE_CHAR;
#ifndef GET_CHAR_IS_BUFFERED
		set_linemode(i);
#else
		was_single = 1;
		if (i->iflags & NOECHO)
		{
			was_noecho = 1;
			i->iflags &= ~NOECHO;
		}
#endif
	}

	copy_and_push_string(str);
	/*
	 * If we have args, we have to push them onto the stack in the order they
	 * were in when we got them.  They will be popped off by the called
	 * function.
	 */
	if (args)
	{
		transfer_push_some_svalues(args, num_arg);
		FREE(args);
	}
	/* current_object no longer set */
	if (function)
	{
		if (function[0] == APPLY___INIT_SPECIAL_CHAR)
			error("Illegal function name.\n");
		(void) apply(function, ob, num_arg + 1, ORIGIN_INTERNAL);
	}
	else
		call_function_pointer(funp, num_arg + 1);

	pop_stack(); /* remove `function' from stack */

#ifdef GET_CHAR_IS_BUFFERED
	if (IP_VALID(i, ob))
	{
		if (was_single && !(i->iflags & SINGLE_CHAR))
		{
			i->text_start = i->text_end = 0;
			i->text[0] = '\0';
			i->iflags &= ~CMD_IN_BUF;
			set_linemode(i);
		}
		if (was_noecho && !(i->iflags & NOECHO))
		add_binary_message(i->ob, telnet_no_echo, sizeof(telnet_no_echo));
	}
#endif

	return (1);
} /* call_function_interactive() */

int set_call(object_t * ob, sentence_t * sent, int flags)
{
	if (ob == 0 || sent == 0)
		return (0);
	if (ob->interactive == 0 || ob->interactive->input_to)
		return (0);
	ob->interactive->input_to = sent;
	ob->interactive->iflags |= (flags & (I_NOECHO | I_NOESC | I_SINGLE_CHAR));
	if (flags & I_NOECHO
	)
		add_binary_message(ob, telnet_yes_echo, sizeof(telnet_yes_echo));
	if (flags & I_SINGLE_CHAR
	)
		set_charmode(ob->interactive);
	return (1);
} /* set_call() */
#endif

void set_prompt(char * str)
{
	if (command_giver && command_giver->interactive)
	{
		command_giver->interactive->prompt = str;
	}
} /* set_prompt() */

/*
 * Print the prompt, but only if input_to not is disabled.
 */
static void print_prompt(interactive_t* ip)
{
	object_t *ob = ip->ob;

#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
	if (ip->input_to == 0)
	{
#endif
		/* give user object a chance to write its own prompt */
		if (!(ip->iflags & HAS_WRITE_PROMPT))
			tell_object(ip->ob, ip->prompt, strlen(ip->prompt));
#ifdef OLD_ED
		else if (ip->ed_buffer)
		tell_object(ip->ob, ip->prompt, strlen(ip->prompt));
#endif
		else if (!apply(APPLY_WRITE_PROMPT, ip->ob, 0, ORIGIN_DRIVER))
		{
			if (!IP_VALID(ip, ob))
				return;
			ip->iflags &= ~HAS_WRITE_PROMPT;
			tell_object(ip->ob, ip->prompt, strlen(ip->prompt));
		}
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
	}
#endif
	if (!IP_VALID(ip, ob))
		return;
	/*
	 * Put the IAC GA thing in here... Moved from before writing the prompt;
	 * vt src says it's a terminator. Should it be inside the no-input_to
	 * case? We'll see, I guess.
	 */
	if ((ip->iflags & USING_TELNET) && !(ip->iflags & SUPPRESS_GA))
		add_binary_message(command_giver, telnet_ga, sizeof(telnet_ga));
	if (!IP_VALID(ip, ob))
		return;
} /* print_prompt() */

/*
 * Let object 'me' snoop object 'you'. If 'you' is 0, then turn off
 * snooping.
 *
 * This routine is almost identical to the old set_snoop. The main
 * difference is that the routine writes nothing to user directly,
 * all such communication is taken care of by the mudlib. It communicates
 * with master.c in order to find out if the operation is permissble or
 * not. The old routine let everyone snoop anyone. This routine also returns
 * 0 or 1 depending on success.
 */
#ifndef NO_SNOOP
int new_set_snoop(object_t * by, object_t * victim)
{
	interactive_t *ip;
	object_t *tmp;

	if (by->flags & O_DESTRUCTED
	)
		return 0;
	if (victim && (victim->flags & O_DESTRUCTED))
		return 0;

	if (victim)
	{
		if (!victim->interactive)
			error("Second argument of snoop() is not interactive!\n");
		ip = victim->interactive;
	}
	else
	{
		/*
		 * Stop snoop.
		 */
		if (by->flags & O_SNOOP)
		{
			int i;

			for (i = 0; i < max_users; i++)
			{
				if (all_users[i] && all_users[i]->snooped_by == by)
					all_users[i]->snooped_by = 0;
			}
			by->flags &= ~O_SNOOP;
		}
		return 1;
	}

	/*
	 * Protect against snooping loops.
	 */
	tmp = by;
	while (tmp)
	{
		if (tmp == victim)
			return 0;

		/* the person snooping us, if any */
		tmp = (tmp->interactive ? tmp->interactive->snooped_by : 0);
	}

	/*
	 * Terminate previous snoop, if any.
	 */
	if (by->flags & O_SNOOP)
	{
		int i;

		for (i = 0; i < max_users; i++)
		{
			if (all_users[i] && all_users[i]->snooped_by == by)
				all_users[i]->snooped_by = 0;
		}
	}
	if (ip->snooped_by)
		ip->snooped_by->flags &= ~O_SNOOP;
	by->flags |= O_SNOOP;
	ip->snooped_by = by;

	return 1;
} /* set_new_snoop() */
#endif

static void query_addr_name(object_t * ob)
{
	static char buf[100];
	static char *dbuf = &buf[sizeof(int) + sizeof(int) + sizeof(int)];
	int msglen;
	int msgtype;

	sprintf(dbuf, "%s", query_ip_number(ob));
	msglen = sizeof(int) + strlen(dbuf) + 1;

	msgtype = DATALEN;
	memcpy(buf, (char *) &msgtype, sizeof(msgtype));
	memcpy(&buf[sizeof(int)], (char *) &msglen, sizeof(msglen));

	msgtype = NAMEBYIP;
	memcpy(&buf[sizeof(int) + sizeof(int)], (char *) &msgtype, sizeof(msgtype));
	debug(connections, ("query_addr_name: sent address server %s\n", dbuf));

	if (OS_socket_write(addr_server_fd, buf, msglen + sizeof(int) + sizeof(int))
	        == -1)
	{
		switch (socket_errno)
		{
			case EBADF:
				debug_message("Address server has closed connection.\n");
				addr_server_fd = -1;
				break;
			default:
				socket_perror("query_addr_name: write", 0);
				break;
		}
	}
} /* query_addr_name() */

#define IPSIZE 200
typedef struct
{
	char *name;
	svalue_t call_back;
	object_t *ob_to_call;
} ipnumberentry_t;

static ipnumberentry_t ipnumbertable[IPSIZE];

/*
 * Does a call back on the current_object with the function call_back.
 */
int query_addr_number(char * name, svalue_t * call_back)
{
	static char buf[100];
	static char *dbuf = &buf[sizeof(int) + sizeof(int) + sizeof(int)];
	int msglen;
	int msgtype;

	if ((addr_server_fd < 0)
	        || (strlen(name)
	                >= 100 - (sizeof(msgtype) + sizeof(msglen) + sizeof(int))))
	{
		share_and_push_string(name);
		push_undefined();
		if (call_back->type == T_STRING
		)
			apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL);
		else
			call_function_pointer(call_back->u.fp, 2);
		return 0;
	}
	strcpy(dbuf, name);
	msglen = sizeof(int) + strlen(name) + 1;

	msgtype = DATALEN;
	memcpy(buf, (char *) &msgtype, sizeof(msgtype));
	memcpy(&buf[sizeof(int)], (char *) &msglen, sizeof(msglen));

	msgtype = (name[0] >= '0' && name[0] <= '9') ? NAMEBYIP : IPBYNAME;
	memcpy(&buf[sizeof(int) + sizeof(int)], (char *) &msgtype, sizeof(msgtype));

	debug(connections, ("query_addr_number: sent address server %s\n", dbuf));

	if (OS_socket_write(addr_server_fd, buf, msglen + sizeof(int) + sizeof(int))
	        == -1)
	{
		switch (socket_errno)
		{
			case EBADF:
				debug_message("Address server has closed connection.\n");
				addr_server_fd = -1;
				break;
			default:
				socket_perror("query_addr_name: write", 0);
				break;
		}
		share_and_push_string(name);
		push_undefined();
		if (call_back->type == T_STRING
		)
			apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL);
		else
			call_function_pointer(call_back->u.fp, 2);
		return 0;
	}
	else
	{
		int i;

		/* We put ourselves into the pending name lookup entry table */
		/* Find the first free entry */
		for (i = 0; i < IPSIZE && ipnumbertable[i].name; i++)
			;
		if (i == IPSIZE)
		{
			/* We need to error...  */
			share_and_push_string(name);
			push_undefined();
			if (call_back->type == T_STRING
			)
				apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL);
			else
				call_function_pointer(call_back->u.fp, 2);
			return 0;
		}
		/* Create our entry... */
		ipnumbertable[i].name = make_shared_string(name);
		assign_svalue_no_free(&ipnumbertable[i].call_back, call_back);
		ipnumbertable[i].ob_to_call = current_object;
		add_ref(current_object, "query_addr_number: ");
		return i + 1;
	}
} /* query_addr_number() */

static void got_addr_number(char * number, char * name)
{
	int i;
	char *theName, *theNumber;

	/* First remove all the dested ones... */
	for (i = 0; i < IPSIZE; i++)
		if (ipnumbertable[i].name
		        && ipnumbertable[i].ob_to_call->flags & O_DESTRUCTED)
		{
			free_svalue(&ipnumbertable[i].call_back, "got_addr_number");
			free_string(ipnumbertable[i].name);
			free_object(ipnumbertable[i].ob_to_call, "got_addr_number: ");
			ipnumbertable[i].name = NULL;
		}
	for (i = 0; i < IPSIZE; i++)
	{
		if (ipnumbertable[i].name && strcmp(name, ipnumbertable[i].name) == 0)
		{
			/* Found one, do the call back... */
			theName = ipnumbertable[i].name;
			theNumber = number;

			if (uisdigit(theName[0]))
			{
				char *tmp;

				tmp = theName;
				theName = theNumber;
				theNumber = tmp;
			}
			if (strcmp(theName, "0"))
			{
				share_and_push_string(theName);
			}
			else
			{
				push_undefined();
			}
			if (strcmp(theNumber, "0"))
			{
				share_and_push_string(theNumber);
			}
			else
			{
				push_undefined();
			}
			push_number(i + 1);
			if (ipnumbertable[i].call_back.type == T_STRING
			)
				safe_apply(ipnumbertable[i].call_back.u.string,
				        ipnumbertable[i].ob_to_call, 3, ORIGIN_INTERNAL);
			else
				safe_call_function_pointer(ipnumbertable[i].call_back.u.fp, 3);
			free_svalue(&ipnumbertable[i].call_back, "got_addr_number");
			free_string(ipnumbertable[i].name);
			free_object(ipnumbertable[i].ob_to_call, "got_addr_number: ");
			ipnumbertable[i].name = NULL;
		}
	}
} /* got_addr_number() */

#undef IPSIZE
#define IPSIZE 200
typedef struct
{
	long addr;
	char *name;
} ipentry_t;

static ipentry_t iptable[IPSIZE];
static int ipcur;

#ifdef DEBUGMALLOC_EXTENSIONS
void mark_iptable()
{
	int i;

	for (i=0; i < IPSIZE; i++)
	if (iptable[i].name)
	EXTRA_REF(BLOCK(iptable[i].name))++;
}
#endif

char *query_ip_name(object_t * ob)
{
	int i;

	if (ob == 0)
		ob = command_giver;
	if (!ob || ob->interactive == 0)
		return NULL;
	for (i = 0; i < IPSIZE; i++)
	{
		if (iptable[i].addr == ob->interactive->addr.sin_addr.s_addr
		        && iptable[i].name)
			return (iptable[i].name);
	}
	return (inet_ntoa(ob->interactive->addr.sin_addr));
}

static void add_ip_entry(long addr, char * name)
{
	int i;

	for (i = 0; i < IPSIZE; i++)
	{
		if (iptable[i].addr == addr)
			return;
	}
	iptable[ipcur].addr = addr;
	if (iptable[ipcur].name)
		free_string(iptable[ipcur].name);
	iptable[ipcur].name = make_shared_string(name);
	ipcur = (ipcur + 1) % IPSIZE;
}

char *query_ip_number(object_t * ob)
{
	if (ob == 0)
		ob = command_giver;
	if (!ob || ob->interactive == 0)
		return 0;
	return (inet_ntoa(ob->interactive->addr.sin_addr));
}

#ifndef INET_NTOA_OK
/*
 * Note: if the address string is "a.b.c.d" the address number is
 *       a * 256^3 + b * 256^2 + c * 256 + d
 */
char *inet_ntoa(struct in_addr ad)
{
	u_long s_ad;
	int a, b, c, d;
	static char addr[20]; /* 16 + 1 should be enough */

	s_ad = ad.s_addr;
	d = s_ad % 256;
	s_ad /= 256;
	c = s_ad % 256;
	s_ad /= 256;
	b = s_ad % 256;
	a = s_ad / 256;
	sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
	return (addr);
}
#endif				/* INET_NTOA_OK */

char *query_host_name()
{
	static char name[40];

	gethostname(name, sizeof(name));
	name[sizeof(name) - 1] = '\0'; /* Just to make sure */
	return (name);
} /* query_host_name() */

#ifndef NO_SNOOP
object_t *query_snoop(object_t * ob)
{
	if (!ob->interactive)
		return 0;
	return ob->interactive->snooped_by;
} /* query_snoop() */

object_t *query_snooping(object_t * ob)
{
	int i;

	if (!(ob->flags & O_SNOOP))
		return 0;
	for (i = 0; i < max_users; i++)
	{
		if (all_users[i] && all_users[i]->snooped_by == ob)
			return all_users[i]->ob;
	}
	fatal("couldn't find snoop target.\n");
	return 0;
} /* query_snooping() */
#endif

int query_idle(object_t * ob)
{
	if (!ob->interactive)
		error("query_idle() of non-interactive object.\n");
	return (current_time - ob->interactive->last_time);
} /* query_idle() */

#ifdef F_EXEC
int replace_interactive(object_t * ob, object_t * obfrom)
{
	if (ob->interactive)
	{
		error("Bad argument 1 to exec()\n");
	}
	if (!obfrom->interactive)
	{
		error("Bad argument 2 to exec()\n");
	}
#ifdef F_SET_HIDE
	if ((ob->flags & O_HIDDEN) != (obfrom->flags & O_HIDDEN))
	{
		if (ob->flags & O_HIDDEN)
		{
			num_hidden_users++;
		}
		else
		{
			num_hidden_users--;
		}
	}
#endif
	ob->interactive = obfrom->interactive;
	/*
	 * assume the existance of write_prompt and process_input in user.c until
	 * proven wrong (after trying to call them).
	 */
	ob->interactive->iflags |= (HAS_WRITE_PROMPT | HAS_PROCESS_INPUT);
	obfrom->interactive = 0;
	ob->interactive->ob = ob;
	ob->flags |= O_ONCE_INTERACTIVE;
	obfrom->flags &= ~O_ONCE_INTERACTIVE;
	add_ref(ob, "exec");
	free_object(obfrom, "exec");
	if (obfrom == command_giver)
	{
		set_command_giver(ob);
	}
	return (1);
} /* replace_interactive() */
#endif

void outbuf_zero(outbuffer_t * outbuf)
{
	outbuf->real_size = 0;
	outbuf->buffer = 0;
}

int outbuf_extend(outbuffer_t * outbuf, int l)
{
	int limit;

	DEBUG_CHECK(l < 0, "Negative length passed to outbuf_extend.\n");
	l = (l > USHRT_MAX ? USHRT_MAX : l);

	if (outbuf->buffer)
	{
		limit = MSTR_SIZE(outbuf->buffer);
		if (outbuf->real_size + l > limit)
		{
			if (outbuf->real_size == USHRT_MAX
			)
				return 0; /* TRUNCATED */

			/* assume it's going to grow some more */
			limit = (outbuf->real_size + l) * 2;
			if (limit > USHRT_MAX)
			{
				limit = USHRT_MAX;
				outbuf->buffer = extend_string(outbuf->buffer, USHRT_MAX);
				return USHRT_MAX - outbuf->real_size;
			}
			outbuf->buffer = extend_string(outbuf->buffer, limit);
		}
	}
	else
	{
		outbuf->buffer = new_string(l, "outbuf_extend");
		outbuf->real_size = 0;
	}
	return l;
}

void outbuf_add(outbuffer_t * outbuf, char * str)
{
	int l, limit;

	if (!outbuf)
		return;
	l = strlen(str);
	if ((limit = outbuf_extend(outbuf, l)) > 0)
	{
		strncpy(outbuf->buffer + outbuf->real_size, str, limit);
		outbuf->real_size += (l > limit ? limit : l);
		*(outbuf->buffer + outbuf->real_size) = 0;
	}
}

void outbuf_addchar(outbuffer_t * outbuf, char c)
{
	if (outbuf && (outbuf_extend(outbuf, 1)) > 0)
	{
		*(outbuf->buffer + outbuf->real_size++) = c;
		*(outbuf->buffer + outbuf->real_size) = 0;
	}
}

void outbuf_addv(outbuffer_t * outbuf, char * format, ...)
{
	char buf[LARGEST_PRINTABLE_STRING + 1];
	va_list args;
	V_DCL(char *format);
	V_DCL(outbuffer_t *outbuf);

	V_START(args, format);
	V_VAR(outbuffer_t *, outbuf, args);
	V_VAR(char *, format, args);

	vsprintf(buf, format, args);
	va_end(args);

	if (!outbuf) return;

	outbuf_add(outbuf, buf);
}

void outbuf_fix(outbuffer_t * outbuf)
{
	if (outbuf && outbuf->buffer)
		outbuf->buffer = extend_string(outbuf->buffer, outbuf->real_size);
}

void outbuf_push(outbuffer_t * outbuf)
{
	STACK_INC;
	sp->type = T_STRING;
	if (outbuf && outbuf->buffer)
	{
		outbuf->buffer = extend_string(outbuf->buffer, outbuf->real_size);

		sp->subtype = STRING_MALLOC;
		sp->u.string = outbuf->buffer;
	}
	else
	{
		sp->subtype = STRING_CONSTANT;
		sp->u.string = "";
	}
}
